﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PMatch.Formats.SequenceSample
{
	public partial class sample
	{
		public sample(string data)
		{
			this.data = data;
		}
	}

	public partial class samples
	{
		public override string ToString()
		{
			return string.Format("Pos:{0} Neg:{1}", positive.Count, negative.Count);
		}

		/// <summary>
		/// Desordena una lista
		/// </summary>		
		public static void RandomSort<T>(IList<T> array, Random random)
		{
			var l = array.Count;
			for (int i = 0; i < l; i += 1)
			{
				int swapIndex = random.Next(i, l);
				if (swapIndex != i)
				{
					T temp = array[i];
					array[i] = array[swapIndex];
					array[swapIndex] = temp;
				}
			}
		}

		/// <summary>
		/// Cantidad total de muestras
		/// </summary>
		public int Count
		{
			get
			{
				return positive.Count + negative.Count;
			}
		}

		/// <summary>
		/// Divide el conjunto de entrada en varios conjuntos disyuntos los cuales 
		/// contienen las cantidades de elementos indicadas en <paramref name="percents"/> en forma porcentual.
		/// El arreglo de salida es de tamaño <paramref name="percents" />.Length+1 , el tamaño
		/// del ultimo conjunto es del tamaño restante a la suma de los elementos de <paramref name="percents" />
		/// </summary>		
		public samples[] SplitDataset(Random random, params decimal[] percents)
		{
			var dataset = this;
			var counts = percents.Select(x => (int)(Count * x)).ToArray();
			return SplitDataset(random, counts);
		}

		/// <summary>
		/// Divide el conjunto de entrada en varios conjuntos disyuntos los cuales 
		/// contienen las cantidades de elementos indicadas en <paramref name="counts"/>.
		/// El arreglo de salida es de tamaño <paramref name="counts" />.Length+1 , el tamaño
		/// del ultimo conjunto es del tamaño restante a la suma de los elementos de <paramref name="counts"/>
		/// </summary>
		/// <example>
		/// Assert(dataset.sample.Count == 1000);
		/// opt = SplitDataset(dataset, rnd, 800);
		/// Assert(opt[0].Length == 800);
		/// Assert(opt[1].Length == 200);
		/// </example>
		/// <param name="dataset">Conjunto de entrada</param>
		/// <param name="random">Generador aleatorio, si es null no se desordenan las muestras</param>
		/// <param name="counts">Cantidades de elementos para cada conjunto de salida</param>		
		public samples[] SplitDataset(Random random, params int[] counts)
		{
			var dataset = this;
			var pSamples = dataset.positive;
			var nSamples = dataset.negative;
			var pTotalCount = pSamples.Count;
			var outputSetsCount = counts.Length + 1;
			var outputDatasets = new samples[outputSetsCount];
			var totalSampleCount = dataset.Count;
			var remainingSampleCount = totalSampleCount;
			for (int i = 0; i < outputSetsCount; i++)
			{
				var ocount = i == outputSetsCount - 1 ? remainingSampleCount : counts[i];
				var opos = ocount * pTotalCount / totalSampleCount;
				var oneg = ocount - opos; // ahora oneg + opos = ocount && opos/oneg = cpos/cneg
				var ds = new samples();
				ds.positive = pSamples.Take(opos).ToList();
				ds.negative = nSamples.Take(oneg).ToList();
				if (random != null)
				{
					RandomSort(ds.positive, random);
					RandomSort(ds.negative, random);
				}
				outputDatasets[i] = ds;
				remainingSampleCount -= ocount;
			}
			return outputDatasets;
		}
	}
}
